量化交易系统
Quantitative Trading System — 从信号到订单的工程链路:数据、特征、策略、回测、风控、执行、对账、复盘的完整体系
0. 系统概览
什么是量化交易系统
量化交易(Quantitative Trading)是用数学模型取代主观判断进行交易决策的交易方式。一个"系统"则是指把这一过程工程化——把数据、特征、策略、回测、风控、执行、对账、复盘八段链路连成一条"不漏数据、不串时间、不丢订单"的流水线。量化交易系统 = 执行 + 验证 + 风控 + 执行质量,缺了后三样,只是自动化的壳。
核心认知:回测 ≠ 实盘
一个量化策略从"在 notebook 里看到一条漂亮的累计收益曲线"走到"在真实账户里持续产生 PnL",至少要穿过四套环境:研究(Research)→ 回测(Backtest)→ 模拟交易(Paper Trading)→ 实盘(Live)。每一步都过滤掉一批"原本以为成立"的假设。
| 环境 | 解决什么问题 | 典型失败模式 |
|---|---|---|
| 研究 Research | 从数据里挖掘规律,形成可编程的假设 | 数据窥视(看着答案找规律) |
| 回测 Backtest | 用历史数据验证策略是否真能赚钱 | 前视偏差、过拟合、幸存者偏差 |
| 模拟盘 Paper | 用实时行情检验回测假设是否成立 | 回测与实盘成交假设不一致(fill rate、滑点) |
| 实盘 Live | 真实资金、真实成交、真实风控 | 订单状态机、对账、事前风控、灰度事故 |
strategy.yaml + 模型工件;回测→模拟 交回测报告 + "与回测引擎同一接口实现"的声明;模拟→实盘 交 N 天 paper 偏差报告 + 风控审计签字;实盘→研究 交每日 PnL 归因 + 异常订单台账 + 滑点对比。这是一个闭环——实盘里学到的东西必须能流回下一次研究,否则四套环境就退化成"三套独立筒仓 + 一个生产环境"。
1. 行为视图(系统做什么)
1.1 用例图:整体行为
先从最宏观的视角看系统对外提供的全部能力,以及谁在使用它们。
| 参与者 | 职责 | 主要用例 |
|---|---|---|
| 研究员 Quant | 发现规律、写成策略 | 数据接入与治理、策略开发、回测与参数优化、模拟盘验证 |
| 交易员/运维 Ops | 跑系统、盯运行 | 模拟盘、实盘交易、订单与持仓管理、监控告警与复盘 |
| 风控官 Risk | 守住风险边界 | 模拟盘、实盘、实时风控与熔断、监管报送 |
| 监管 Regulator | 合规审计 | 监管报送 |
1.2 活动图:从想法到实盘订单
围绕最核心的主流程逐步细化:一个策略想法如何穿过四套环境,最终变成一笔实盘订单,并形成闭环反馈。
把这段流程落到工程上,关键是"环境差异收敛到适配器"。下面是最小可运行的跨环境数据接口演示:
# 环境适配器:同一接口,不同实现
class DataSource(ABC):
@abstractmethod
def get_history(self, symbol, start, end): ...
class BacktestSource(DataSource): # 读历史仓库,禁未来数据
def get_history(self, symbol, start, end):
df = hist_store.query(symbol, start, end)
return df.shift(1) # 信号只用 T-1 及以前信息(防前视)
class LiveSource(DataSource): # 接实时行情
def get_history(self, symbol, start, end):
return live_feed.bars(symbol, start, end)
1.3 时序图:一次实盘下单的完整交互
主流程里"订单执行"这一段,涉及策略、风控、OMS、EMS、券商、交易所多个组件。时序图把它们之间的消息往来按时间展开。
对应到代码,就是 Broker 抽象接口 + OMS 状态更新:
# 执行层:统一 Broker 接口
class Broker(ABC):
def submit_order(self, order): ...
def get_order_status(self, order_id): ...
def cancel_order(self, order_id): ...
# 实盘下单流程(伪代码)
if risk.pre_trade_check(order): # 事前风控在订单送出前拦截
oid = broker.submit_order(order) # 不假设成交,等待交易所回报
while True:
st = broker.get_order_status(oid)
if st in (FILLED, CANCELLED, REJECTED): break
if timeout: broker.cancel_order(oid); break
sleep(0.1)
return st # 成交结果必须回写给策略层
1.4 状态机:订单生命周期
订单从创建到终态,经历一组确定的合法状态迁移。状态机是 OMS 的骨架——状态机写对了,断线恢复、超时撤单、部分成交才有依据。
# OMS 订单状态机(简化实现)
ORDER_STATES = ["PENDING", "PARTIAL", "FILLED", "CANCELLED", "REJECTED"]
TRANSITIONS = {
"PENDING": {"PARTIAL", "FILLED", "CANCELLED", "REJECTED"},
"PARTIAL": {"FILLED", "CANCELLED"},
}
def transition(order, new_state):
if new_state in TRANSITIONS[order.status]:
order.status = new_state
audit_log(order)
else:
raise IllegalState(f"{order.status} -> {new_state}")
2. 结构视图(系统由什么组成)
2.1 部署图:四套环境拓扑
从宏观架构看系统部署:四套环境各自独立部署,共享一套数据服务、配置中心与监控设施。
2.2 组件图:分层架构(六层核心抽象)
四套环境之所以能复用同一份策略代码,靠的是把整个交易系统垂直切成一组接口:策略只依赖接口,不依赖实现。这套六层抽象是文档最重要的架构决策。
| 层 | 接口职责 | 实盘与回测的差异 |
|---|---|---|
| 数据接入 Data | 提供行情/历史数据,格式统一 | 回测读历史仓库(防未来函数);实盘接实时流(处理延迟、保证连续) |
| 特征 Feature | 把原始 bar 转成特征向量 | 两边一致(同一套特征定义) |
| 信号 Signal | 根据行情 + 持仓生成买卖信号 | 两边一致(只返回信号,不下单) |
| 组合 Portfolio | 目标权重、调仓、持仓跟踪 | 回测虚拟撮合 vs 实盘真实持仓回报 |
| 订单 Order | 订单模型 + OMS 状态机 | 实盘才有 tick size / lot size / 停牌限制 |
| 风险 Risk | 事前风控 + 实时监控 | 实盘要求毫秒级事前检查;回测可事后看 |
2.3 类图:核心抽象
向下细化到内部细节:核心类及其依赖关系。这是"六层抽象"的代码形态。
# 三套 Broker 实现:策略代码完全无感
class BacktestBroker(Broker):
def submit_order(self, order):
return fill_model(order, slippage_model=TWAP_SLIPPAGE)
class PaperBroker(Broker):
def submit_order(self, order):
return virtual_exchange.match(order) # 按实时价模拟成交
class LiveBroker(Broker):
def submit_order(self, order):
return api.submit(order) # QMT / xtquant / ccxt / FIX
3. 六大核心模块详解
3.1 数据层
数据是量化系统的"原料"。数据层负责获取、清洗、对齐、存储,同时保证两点铁律:数据必须可信(无未来函数、无脏数据)、必须可复现(回测与实盘拿到的数据格式一致)。
| 子能力 | 要点 |
|---|---|
| 数据源接入 | 行情(OHLCV)、基本面、另类数据(舆情/卫星/交易流水);数据商如 Tushare/AKShare/baostock/ccxt;统一抽象 DataSource → get_history() |
| 清洗与复权 | 剔除停牌/ST、处理涨跌停;前复权 vs 后复权口径统一(用 adj_factor 字段) |
| PIT 数据 | Point-In-Time(时点)数据:基本面字段必须"到发布日才可见",禁止用后补数据回填 |
| 存储 | 日频用关系库(MySQL 按年分区);分钟级/高频用时序库(InfluxDB/TimescaleDB/ClickHouse);缓存层 Redis |
| 幸存者池 | 股票池必须含历史退市/停牌股票,否则回测天然高估(幸存者偏差 Survivorship Bias) |
3.2 策略层
策略层承载信号生成与组合构建。它是"策略注册中心"——每个策略实现统一接口、声明参数,系统自动发现、自动映射到仪表盘。策略逻辑本身是各异的,但接口必须统一。
# 策略统一接口(注册中心模式)
class Strategy(ABC):
params: dict
@abstractmethod
def on_bar(self, bar): ...
@abstractmethod
def on_signal(self, signal) -> SignalDecision: ...
def target_weights(self) -> dict: # 组合层要的目标权重
return {...}
# 示例:双均线交叉
class DualMA(Strategy):
def on_bar(self, bar):
fast = sma(bar, self.params["fast"])
slow = sma(bar, self.params["slow"])
if cross_up(fast, slow): return SignalDecision(BUY, weight=0.5)
3.3 回测引擎
回测引擎只保证"语法对",真正杀死策略的是"逻辑错、数据脏、推断不严"。回测引擎的核心是事件驱动:MarketEvent → Signal → Order → Fill → 绩效报告,逐事件推进,与实盘同一套路径。
3.4 风控层
风控是量化系统与普通程序的最大区别。它必须独立于策略代码运行,在订单网关与策略之间,毫秒级完成检查。风控分三个时刻:事前(pre-trade)、事中(in-flight)、事后(post-trade)。
| 时刻 | 检查项 | 触发动作 |
|---|---|---|
| 事前 订单送出前 | 单笔订单金额上限 | 拒单 + 审计留痕 |
| 单标的累计敞口上限 | 拒超限开仓 | |
| 账户 gross/net 敞口上限 | 暂停新开仓 | |
| 单位时间订单速率上限(防 runaway) | 限速 / 熔断 | |
| 价格偏离参考价百分比(防 fat finger)、黑名单 | 拒单 | |
| 事中 | 回撤、日亏损、集中度实时监控 | 暂停所有新开仓 / 强制平仓 |
| 事后 | 盘后对账、偏差分析 | 留痕 + 归因,反馈研究 |
# 事前风控:独立于策略进程的规则表
RULES = [ # (scope, key, limit)
("account", "gross_exposure", 1_000_000),
("symbol", "position_pct", 0.30),
("account", "max_drawdown", 0.20),
("account", "daily_loss", 0.05),
]
def pre_trade_check(order, state) -> bool:
for scope, key, limit in RULES:
val = state.get(scope, order, key)
if val > limit:
audit(order, f"{scope}.{key} {val}>{limit}") # 任何规则触发立刻留痕
return False
return True
3.5 执行层
执行层把"风控放行的订单"变成"交易所里的成交"。最容易忽略的坑:不假设一定能成交。回测里下买单默认下一秒成交;实盘可能有流动性不足、滑点、网络延迟。
| 子模块 | 职责 | 要点 |
|---|---|---|
| OMS 订单管理 | 订单状态机、超时撤单、改单重发 | 状态机见 §1.4;等待交易所确认而非假设成交 |
| EMS 执行算法 | TWAP / VWAP / 冰山单拆单 | 大单拆小单,降低市场冲击;执行质量直接影响 PnL |
| 券商接口 | FIX 协议 / 券商 API | FIX 是电子交易行业标准协议;协议翻译、断线重连 |
| 对账 Recon | 本地状态 vs 券商/交易所状态比对 | 差异写入 reconciliation 日志;未完成对账前禁止发新单(hold-on) |
3.6 监控与复盘
实盘不是"挂上就跑"。工程重点四大件:订单状态机、对账、事前风控、灰度发布——这四件事任何一件没做扎实,策略再好也会被生产路径上的事故抹平。
| 能力 | 内容 |
|---|---|
| 实时监控 | 持仓、敞口、PnL、订单状态、行情延迟;异常触发短信/电话告警 |
| 绩效归因 | 每日 PnL 归因(市场/行业/因子/残差),回答"钱从哪里赚/亏的" |
| 异常台账 | 异常订单、风控触发记录、滑点对比——全部留痕可审计 |
| 复盘闭环 | 实盘结果回馈研究:修正滑点假设、特征定义,进入下一轮回测 |
4. 实战操作手册(从零搭建一个量化系统)
以下按"最小可用 + 渐进完善"路径,适合个人/小团队从零搭建。技术栈以 Python 生态为例(研究/低频策略场景),高频场景需 C++/FPGA 另行考量。
4.1 技术栈选型
| 层 | 推荐选型 | 理由 |
|---|---|---|
| 数据 | AKShare(免费)/ Tushare Pro / baostock;ccxt(加密) | 无需 Token 或低成本,可插拔抽象 |
| 存储 | MySQL / SQLite(日频)→ 后续 ClickHouse | 日频数据量小,关系库够用 |
| 回测 | 自研事件驱动引擎 / Backtrader / Qlib | 自研才能保证回测=实盘同接口 |
| 风控 | 独立模块 risk/(allocator + sizing + stops) | 与策略进程隔离,独立风控守护 |
| 执行 | 模拟盘 PaperBroker → 实盘 QMT/xtquant(A股) | 先跑通模拟盘再碰实盘 |
| 界面 | Streamlit 仪表盘(策略/回测/风控/模拟盘页) | 策略参数自动映射为滑块 |
4.2 搭建步骤
- 建目录骨架:
data/ strategies/ backtest/ risk/ execution/ dashboard/ tests/——对应 §2.2 的六层抽象。 - 写数据适配器:实现
DataSource.get_history(),先接一个免费源,存本地 SQLite。 - 写第一个策略:双均线交叉(§3.2 示例),实现
Strategy接口。 - 写事件驱动回测引擎:MarketEvent → Signal → Order → Fill → 报告(§3.3),含滑点/手续费模型。
- 写风控模块:仓位(等权/Kelly/波动率目标)、止损(固定/ATR/移动)、独立风控守护(§3.4)。
- 跑模拟盘:PaperBroker 按实时价模拟成交,与回测对比 PnL 偏差(§1.2 活动图的"偏差可接受?"卡点)。
- 灰度实盘:小额资金 → 事前风控开启 → 逐步放量 → 随时可回退(§2.1)。
- 复盘闭环:每日 PnL 归因 + 滑点校准,把结论写回研究(§3.6)。
4.3 常见踩坑与对策
| 坑 | 症状 | 对策 |
|---|---|---|
| 回测一套代码、实盘另一套 | 研究字段实盘是 nan,实盘限价单被拒 | 六层接口统一 + 适配器收敛差异(§2.2) |
| 假设一定成交 | 实盘成交率远低于回测 | 不假设成交,等回报,超时撤单(§1.3) |
| 风控放在执行之后 | 订单已发出才被拦 | 风控必须在策略层与执行层之间、下单之前(§3.4) |
| 涨跌停/tick size 没校验 | 实盘订单被拒 | Broker 层加规则校验(A股 T+1、涨跌停、最小单位) |
5. 参数指令速查手册
5.1 核心接口速查
| 接口 | 方法 | 说明 |
|---|---|---|
DataSource | get_history(symbol, start, end)get_quotes(symbols) | 统一数据入口,回测/实盘同格式 |
Strategy | on_bar(bar)on_signal(signal)target_weights() | 策略只产出信号/权重,不下单 |
RiskManager | pre_trade_check(order)exposure(account) | 独立进程,毫秒级事前检查 |
Broker | submit_order / get_order_status / cancel_order / get_positions / get_balance | 回测/模拟/实盘三套实现 |
OMS | 订单状态机 + 超时撤单 + 改单 | 订单状态权威记录 |
5.2 订单类型速查
| 类型 | 语义 | 适用 |
|---|---|---|
| 市价单 Market | 立即按当前最优价成交 | 确定性优先(止损、平仓) |
| 限价单 Limit | 以指定价或更优价成交,不保证成交 | 控制成本(入场) |
| 止损单 Stop | 价格触及触发价后转市价 | 止损、趋势突破 |
| 条件单 / OCO | 触发条件才报单;一个成交则撤另一个 | 止盈止损配对 |
| 算法单(TWAP/VWAP) | 拆单执行 | 大单降冲击 |
5.3 风控规则参数速查
| 参数 (scope, key) | 典型默认值 | 触发动作 |
|---|---|---|
| account.gross_exposure | ≤ 账户权益上限 | 拒开仓 |
| symbol.position_pct | ≤ 30% | 拒超限开仓 |
| account.max_drawdown | ≤ 20% | 暂停全部新开仓 |
| account.daily_loss | ≤ 5% | 当日停止 / 强制减仓 |
| order.max_amount | 单笔上限 | 拒单(防 fat finger) |
| order.max_rate | 单位时间订单数上限 | 限速 / 熔断(防 runaway) |
| price.max_deviation | 偏离参考价 ±N% | 拒单 |
5.4 仓位方法速查
| 方法 | 公式/思想 | 适用 |
|---|---|---|
| 固定比例 | 每笔固定占比 | 简单稳健 |
| Kelly 公式 | f* = p - q/b(半 Kelly 上限 ~25%) | 有胜率优势时,防过度下注 |
| 风险平价 | 按波动率倒数分配 | 多资产组合 |
| 波动率目标 | 仓位 ∝ 目标波动/实际波动 | 控制组合波动率 |
6. 开源实现对照
有没有开源项目"完整实现"了本文档描述的一套量化系统?答案是:有,但"完整"要按市场与场景拆开看。没有任何开源项目 100% 覆盖"对账、绩效归因、灰度发布、Kill Switch 兜底平仓"这类生产级运维——这些通常仍是自建部分。这正说明为什么商业量化公司普遍"框架开源、运营闭源"。
6.1 主流开源项目一览
| 项目 | 市场 | 语言 | 覆盖度 | 定位 |
|---|---|---|---|---|
| vn.py (VeighNa) | A股/期货/期权/加密 | Python | ★★★★★ | 国内最全:40+ gateway(CTP/XTP/IB/币安)+ CTA回测/实盘 + 算法交易 + 价差/期权 + risk_manager 风控 + RPC 分布式;4.0 新增 vnpy.alpha(因子/ML 投研) |
| Nautilus Trader | 多资产 | Rust+Python | ★★★★★ | 事件驱动、机构级低延迟,回测与实盘同接口,内建风控引擎,架构最接近"生产级" |
| QuantConnect LEAN | 全球多资产 | C#/Python | ★★★★☆ | 功能最全(37.5万+实盘策略部署),实盘通常走 QuantConnect 云/券商,自托管 live 门槛高 |
| Freqtrade | 加密 only | Python | ★★★★☆ | 加密最完整:回测+超参优化+实盘+Telegram 控制+止损 |
| Qlib (微软) | 研究向 | Python | ★★★☆☆ | ML 全流程(因子→模型→回测→组合→订单生成),实盘下单/执行不在范围内 |
| QUANTAXIS | 多市场 | Python/Rust | ★★★★☆ | 数据→因子→回测→实盘→微服务一站式,MongoDB/ClickHouse + RabbitMQ 分布式 |
| Backtrader | 多市场 | Python | ★★☆☆☆ | 经典回测框架(已停止更新),仅回测+部分实盘;仍是业界标杆 |
| Zipline-Reloaded | 多市场 | Python | ★★☆☆☆ | Quantopian 遗产社区维护版,研究/回测向,与 Pandas 深度集成 |
| Hummingbot | 加密 only | Python | ★★★☆☆ | 做市专用(CEX/DEX),实时做市与套利 |
6.2 与本文档架构的模块映射
把各项目对照本文档 §2.2 的六层抽象 + §3 的生产级运维模块,覆盖情况如下(●=完整实现,◐=部分/需自补,○=缺失/范围外):
| 项目 | 数据 | 因子/特征 | 信号/策略 | 回测 | 组合/仓位 | 订单执行 | 风控 | 对账/监控 |
|---|---|---|---|---|---|---|---|---|
| vn.py | ● | ◐ (vnpy.alpha) | ● | ● | ◐ | ● | ● | ◐ |
| Nautilus Trader | ● | ◐ | ● | ● | ◐ | ● | ● | ◐ |
| QuantConnect LEAN | ● | ◐ | ● | ● | ● | ● | ◐ | ◐ |
| Freqtrade | ● (加密) | ◐ | ● | ● | ◐ | ● (加密) | ◐ | ◐ |
| Qlib | ● | ● | ● (ML) | ● | ● | ○ (只生成订单) | ○ | ○ |
| QUANTAXIS | ● | ● | ● | ● | ◐ | ● | ◐ | ◐ |
6.3 生态资源索引:awesome-quant 精选
§6.1 解决"整框架选谁",本节解决"组件/工具选谁"。awesome-quant(wilsonfreitas/awesome-quant,29.4k+ stars)是量化金融开源库的策展目录,按约 19 大主题收录 Python/R/Julia/Java/Rust 等各语言项目,适合当作"找工具的入口"。以下按本文档六层架构精选高频库(与 §6.1 框架级条目不重复):
数据层:行情源与存储
| 库 | 语言 | 用途 |
|---|---|---|
| yfinance | Python | Yahoo Finance 行情/基本面免费数据,研究与原型最常用 |
| ccxt | Python | 100+ 加密交易所统一 API,行情+下单,实盘/回测皆可 |
| akshare / tushare | Python | A股/港美股数据源(akshare 免费聚合、tushare 积分制),覆盖行情/财务/宏观 |
| pandas-datareader | Python | 多来源财经数据统一接口(FRED/Yahoo/Stooq 等) |
| ArcticDB | Python | 时序/逐笔 tick 高频存储,适合自建数据仓库 |
特征/因子层:数值、统计与时序建模
| 库 | 语言 | 用途 |
|---|---|---|
| pandas / polars | Python | DataFrame 基础设施;polars 面向大数据量/向量化回测性能 |
| statsmodels | Python | 统计建模:回归、协整、OLS/GLS、假设检验 |
| ARCH | Python | ARCH/GARCH 族波动率建模 |
| pmdarima | Python | ARIMA/SARIMA 自动定阶(auto_arima) |
技术指标
| 库 | 语言 | 用途 |
|---|---|---|
| TA-Lib | Python | 最全指标库(150+ 指标),C 核心性能高,安装略重 |
| ta | Python | 纯 pandas 实现,轻量易装 |
| finta / pandas_talib | Python | 常见指标 pandas 实现,适合快速原型 |
| TTR | R | R 生态技术指标/交易规则标准库 |
回测引擎(§6.1 之外的补充)
| 库 | 语言 | 用途 |
|---|---|---|
| Backtesting.py | Python | 轻量事件驱动回测,API 简洁、出图友好,个人研究首选 |
| vectorbt | Python | NumPy 向量化回测,可并行扫描海量参数组合 |
| bt | Python | 组合级回测(alignment-based),适合多资产轮动研究 |
| pyqstrat | Python | 快速、可扩展、透明,含执行/成本建模 |
| purgedcv | Python | 去泄漏交叉验证(CPCV、walk-forward)、回测过拟合统计(PBO、夏普收缩),对应 §4.2 防过拟合方法 |
| quantstats | Python | 绩效报告 HTML/图(回撤、夏普、最大回撤、月度热力图) |
组合优化与风控
| 库 | 语言 | 用途 |
|---|---|---|
| PyPortfolioOpt | Python | 均值方差/Black-Litterman/风险平价,scipy 求解 |
| Riskfolio-Lib | Python | 组合优化与风险管理库,支持 CVaR/CDaR/最大回撤约束等前沿目标 |
| ffn | Python | 金融指标函数库:收益/回撤/夏普/绩效对比 |
期权/衍生品定价(研究向工具)
| 库 | 语言 | 用途 |
|---|---|---|
| PyQL | Python | QuantLib 的 Python 封装,金融工程全功能 |
| FinancePy | Python | 固收/权益/FX/信用衍生品定价与风险管理 |
| gs-quant | Python | 高盛开源的量化工具包(定价/风险/数据集) |
| vollib | Python | 期权定价+隐含波动率+Greeks,轻量 |
| tf-quant-finance | Python | TensorFlow 高性能衍生品定价/蒙特卡洛 |
实盘/执行(§6.1 之外的补充)
| 库 | 语言 | 用途 |
|---|---|---|
| pylivetrader | Python | zipline 兼容接口的实盘库,回测→实盘平滑迁移 |
| algobroker | Python | 算法交易执行引擎(对接券商/交易所) |
| aat | Python | 异步算法交易引擎,研究/回测/实盘统一 |
| Blankly | Python | 回测+模拟+实盘一体,多券商/交易所接入 |
附录:监管与合规
量化/程序化交易受所在司法辖区监管,上线前必须单独审计:
| 辖区 | 法规 | 核心要求 |
|---|---|---|
| 美国 | SEC Rule 15c3-5(Market Access) | 订单送出前完成净敞口、单笔金额、错指令(fat-finger)检查 |
| 美国 | Reg SCI | 系统合规与完整性:事件报告、故障恢复、测试要求 |
| 欧盟 | MiFID II RTS 6 | 算法交易治理:事前风控、Kill Switch、监管报送、压力测试 |
| 中国 | 证监会《程序化交易管理规定》 | 程序化交易报备、事前风控要求、异常交易监控 |
文末提示:本文聚焦工程架构,不构成投资建议或交易系统认证;文中接口、状态机、阈值仅用于阐释方法论,模拟盘的盈利从来不能保证实盘可复制。